package bit_poi.service;

import bit_poi.dao.UserClassMapper;
import bit_poi.domain.User;
import bit_poi.utils.CommonUtils;
import bit_poi.utils.MyBatisUtils;
import bit_poi.utils.POIUtils;
import cn.hutool.core.lang.Console;
import org.apache.ibatis.session.SqlSession;
import org.apache.poi.util.StringUtil;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author yuisama
 * @date 2023/01/04 15:19
 * 学生缺勤查询服务
 **/
public class StudentAbsenceService {
    private SqlSession session;
    private String inputFileName;
    private UserClassMapper userClassMapper;
    private Semaphore semaphore;

    public StudentAbsenceService() {
        // 初始化dao
        session = MyBatisUtils.getSqlSession();
        userClassMapper = session.getMapper(UserClassMapper.class);
        semaphore = new Semaphore(0);
    }

    /**
     * 根据学生表格和查询时间查询学生是否缺勤，输出缺勤表格
     */
    public void queryStudentAbsencesAndPrint(String queryTime) {
        // 1.获取学生信息表格
        inputFileName = CommonUtils.PROPS.getStr("fileInputName");
        if (StringUtil.isBlank(inputFileName)) {
            Console.error("未提供学生信息，查询失败");
            return;
        }
        // 2.从学生信息excel表中加载姓名手机号
        String fileName = CommonUtils.DESKTOP + File.separator + inputFileName;
        List<User> students = POIUtils.readFromExcel(fileName);
        // 3.从数据库中查询这些学生的到课状态，获取所有缺勤的学生列表(bean lists)
        List<User> absentStudents = queryAbsentStudentsFromDB(students, queryTime);
        // 4.将缺勤学生写回输出表格
        boolean isCompleted = POIUtils.writeAbsentStudents2Excel(absentStudents, queryTime);
        if (isCompleted) {
            Console.log("缺勤学生数据写回完毕");
        }else {
            Console.error("缺勤学生输出有误，请排查问题");
        }
    }

    /**
     * 从数据库中过滤出所有缺勤学生，组装输出bean lists
     *
     * @param students
     * @param queryTime
     * @return
     */
    private List<User> queryAbsentStudentsFromDB(List<User> students, String queryTime) {
        List<User> absentStudents = new ArrayList<>();
        // 遍历输入表格，过滤所有缺勤学生
        for (User user : students) {
            // 1.先根据每个输入学生手机号反查userId和classId
            user.setInfoFromDB(userClassMapper.getUserInfoByPhoneNum(user.getPhoneNum()));
            // 处理手机号无效的问题
            if (user.getUserId() == null) {
                Console.error(user.getPhoneNum() + "有误！请检查手机号是否有效！");
                continue;
            }
            // 2.再根据userId，classId，queryTime查询到课情况
            Integer userId = user.getUserId();
            Integer classId = user.getClassId();
            String str = userClassMapper.queryStudentIsAbsent(userId,classId,queryTime);
            if (StringUtil.isNotBlank(str)) {
                // 该学生缺课，组装到结果集中
                absentStudents.add(user);
            }
        }
        return absentStudents;
    }

    /**
     * 多线程版查询
     * @param students
     * @param queryTime
     * @return
     */
    private List<User> queryAbsentStudentsFromDBByThread(List<User> students, String queryTime) {
        List<User> absentStudents = new ArrayList<>();
        ExecutorService executor = Executors.newCachedThreadPool();
        AtomicInteger threadCount = new AtomicInteger();
        // 遍历输入表格，过滤所有缺勤学生
        for (User user : students) {
            // 每个学生的查询交给子线程
            executor.submit(() -> {
                threadCount.incrementAndGet();
                SqlSession session = MyBatisUtils.getSqlSession();
                UserClassMapper userClassMapper = session.getMapper(UserClassMapper.class);
                // 1.先根据每个输入学生手机号反查userId和classId
                user.setInfoFromDB(userClassMapper.getUserInfoByPhoneNum(user.getPhoneNum()));
                // 2.再根据userId，classId，queryTime查询到课情况
                Integer userId = user.getUserId();
                Integer classId = user.getClassId();
                String str = userClassMapper.queryStudentIsAbsent(userId,classId,queryTime);
                if (StringUtil.isNotBlank(str)) {
                    // 该学生缺课，组装到结果集中
                    absentStudents.add(user);
                }
                threadCount.decrementAndGet();
                session.close();
                if (threadCount.get() == 0) {
                    semaphore.release();
                    Console.log("所有线程扫描完毕");
                }
            });
        }
        try {
            // 等所有线程查询完毕再返回方法
            semaphore.acquire();
            executor.shutdown();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return absentStudents;
    }

    public void closeSession() {
        this.session.close();
    }

}
